<?php
namespace Zxf5115\Laravel\Kernel\Builders;

use Illuminate\Database\Eloquent\Builder;

/**
 * @author zhangxiaofei [<1326336909@qq.com>]
 * @dateTime 2025-09-21
 *
 * 请求处理器类
 */
class WhereBuilder
{
  /**
   * 当前操作模型对象
   */
  protected $query;

  /**
   * 服务类名称
   */
  protected $serviceClass;

  /**
   * 初始化请求处理器类
   *
   * @param Builder $query 当前操作模型对象
   * @param string $serviceParams 服务类可以访问参数
   */
  public function __construct(Builder $query, array $serviceParams)
  {
    $this->query = $query;

    $this->serviceParams = $serviceParams;
  }


  /**
   * 获取当前服务实例
   *
   * @param Builder $query 当前操作模型对象
   * @param string $serviceParams 服务类可以访问参数
   * @return self
   */
  public static function myself(Builder $query, array $serviceParams) : self
  {
    // 使用基础服务类
    return new self($query, $serviceParams);
  }


  /**
   * 处理请求并生成查询条件
   *
   * @param Builder $query 当前操作模型对象
   * @param array $params 请求数据
   */
  public function buildWhere(array $params) : void
  {
    // 处理默认查询条件
    $this->handleDefaultWhere();

    foreach($params as $paramKey => $paramValue)
    {
      // 判断是否应该跳过当前值
      if($this->shouldSkipParam($paramValue))
      {
        continue;
      }

      // 处理单个请求参数
      $this->handleSingleParam($paramKey, $paramValue);
    }
  }


  /**
   * 判断是否应该跳过当前值
   *
   * @param mixed $paramValue 用户请求参数值
   * @return bool 是否跳过
   */
  private function shouldSkipParam(mixed $paramValue): bool
  {
    return $paramValue === null || $paramValue === '' || (is_array($paramValue) && empty($paramValue)) || (is_string($paramValue) && trim($paramValue) === '');
  }


  /**
   * 处理单个请求参数
   *
   * @param string $paramKey 用户请求参数名
   * @param mixed $paramValue 用户请求参数值
   * @return [type]
   */
  private function handleSingleParam(string $paramKey, mixed $paramValue): void
  {
    // 获取参数处理器配置
    $paramHandlers = $this->getParamHandlers();

    // 按优先级排序处理器
    $sortedHandlers = $this->sortHandlersByPriority($paramHandlers);

    foreach($sortedHandlers as $handler)
    {
      if (method_exists($this, $handler['method']) && $this->{$handler['method']}($paramKey, $paramValue))
      {
        // 匹配到处理器即中断，避免重复处理
        break;
      }
    }
  }


  /**
   * 获取参数处理器配置
   *
   * @return array 处理器配置
   */
  private function getParamHandlers(): array
  {
    return [
      [
        'method' => 'handleBasicWhere',
        'priority' => 10,
        'desc' => '处理普通等值/模糊/范围查询条件'
      ], [
        'method' => 'handleWhereIn',
        'priority' => 20,
        'desc' => '处理以id结尾的参数的IN查询'
      ], [
        'method' => 'handleKeywordMatch',
        'priority' => 30,
        'desc' => '处理多字段关键字模糊匹配'
      ], [
        'method' => 'handleRelationWhere',
        'priority' => 40,
        'desc' => '处理关联模型的查询条件'
      ]
    ];
  }


  /**
   * 按优先级排序处理器
   *
   * @param array $handlers 未排序的处理器配置
   * @return array 按优先级升序排序后的处理器
   */
  private function sortHandlersByPriority(array $handlers) : array
  {
    $priorities = array_column($handlers, 'priority');

    array_multisort($priorities, SORT_ASC, $handlers);

    return $handlers;
  }


  /**
   * 处理默认查询条件
   */
  private function handleDefaultWhere()
  {
    $defaultWhere = $this->serviceParams['defaultWhere'] ?? [];

    // 判断参数是否允许
    if(empty($defaultWhere))
    {
      return false;
    }

    $this->query->where($defaultWhere);
  }


  /**
   * 处理普通查询条件
   *
   * @param string $paramKey 用户请求参数名
   * @param mixed $paramValue 用户请求参数值
   * @return bool 是否处理成功
   */
  private function handleBasicWhere(string $paramKey, mixed $paramValue): bool
  {
    $allowableParams = $this->serviceParams['allowParams'] ?? [];

    // 判断参数是否允许
    if (empty($allowableParams) || !$this->isParamAllowable($allowableParams, $paramKey))
    {
      return false;
    }

    // 构建单个查询条件
    $whereCondition = $this->buildWhereCondition($paramKey, $paramValue);

    if(!empty($whereCondition))
    {
      $this->query->where($whereCondition);
    }

    return true;
  }


  /**
   * 处理WhereIn查询条件
   *
   * @param string $paramKey 用户请求参数名
   * @param mixed $paramValue 用户请求参数值
   * @return bool 是否处理成功
   */
  private function handleWhereIn(string $paramKey, mixed $paramValue): bool
  {
    // 只处理用户请求参数名称以id结尾的参数
    if(!str_ends_with($paramKey, 'id'))
    {
      return false;
    }

    // 解析whereIn查询的参数值
    $inValues = $this->parseInValues($paramValue);

    if(!empty($inValues))
    {
      $this->query->whereIn($paramKey, $inValues);
    }

    return true;
  }


  /**
   * 处理关键字多字段匹配
   *
   * @param string $paramKey 用户请求参数名
   * @param mixed $paramValue 用户请求参数值
   * @return bool 是否处理成功
   */
  private function handleKeywordMatch(string $paramKey, mixed $paramValue): bool
  {
    $keywordConfig = $this->serviceParams['allowKeywordParams'] ?? [];

    if(empty($keywordConfig) || $paramKey !== ($keywordConfig['receive'] ?? ''))
    {
      return false;
    }

    // 多条件查询时，如果使用OR查询，必须使用闭包包裹
    $this->query->where(function ($query) use ($keywordConfig, $paramValue) {
      foreach ($keywordConfig['match'] ?? [] as $matchField)
      {
        $query->where($matchField, 'like', sprintf('%%%s%%', $paramValue), 'or');
      }
    });

    return true;
  }


  /**
   * 处理关联查询条件
   *
   * @param string $paramKey 用户请求参数名
   * @param mixed $paramValue 用户请求参数值
   * @return bool 是否处理成功
   */
  private function handleRelationWhere(string $paramKey, mixed $paramValue): bool
  {
    $relationConfig = $this->serviceParams['allowRelationParams'] ?? [];

    if(empty($relationConfig))
    {
      return false;
    }

    foreach($relationConfig as $relationName => $allowableFields)
    {
      $prefix = sprintf('%s__', $relationName);

      // 判断是否是以关联对象开头的请求参数名称
      if(!str_starts_with($paramKey, $prefix))
      {
        continue;
      }

      $fieldName = substr($paramKey, strlen($prefix));

      if(!$this->isParamAllowable($allowableFields, $fieldName))
      {
        continue;
      }

      // 构建单个查询条件
      $whereCondition = $this->buildWhereCondition($fieldName, $paramValue);

      if(!empty($whereCondition))
      {
        $this->query->whereHasIn($relationName, function($query) use ($whereCondition) {
          $query->where($whereCondition);
        });
      }

      return true;
    }

    return false;
  }


  /**
   * 构建单个查询条件
   *
   * @param string $field 字段名
   * @param mixed $value 字段值
   * @return array|bool 结构化条件（失败返回false）
   */
  private function buildWhereCondition(string $field, mixed $value) : array|bool
  {
    $filterConfig = $this->serviceParams['filterRules'] ?? [];

    // 模糊查询处理
    if($this->isFuzzyFilter($filterConfig, $field))
    {
      return [[$field, 'like', sprintf('%%%s%%', $value)]];
    }

    // 左模糊查询处理
    if($this->isLeftFuzzyFilter($filterConfig, $field))
    {
      return [[$field, 'like', sprintf('%%%s%%', $value)]];
    }

    // 时间范围查询处理
    if($this->isRangeFilter($filterConfig, $field))
    {
      // 构建时间范围查询条件
      return $this->buildRangeCondition($field, $value);
    }

    // 普通等值查询
    return [$field => $value];
  }


  /**
   * 解析whereIn查询的参数值
   *
   * @param mixed $value 用户请求参数值
   * @return array 解析后的whereIn值列表
   */
  private function parseInValues(mixed $value): array
  {
    if(is_array($value))
    {
      return array_filter($value);
    }

    if(is_string($value))
    {
      $trimmedValue = trim($value, ',');

      return $trimmedValue === '' ? [] : array_filter(explode(',', $trimmedValue));
    }

    return $value !== '' && $value !== null ? [$value] : [];
  }


  /**
   * 构建时间范围查询条件
   *
   * @param string $field 字段名
   * @param mixed $value 原始范围值
   * @return array 结构化范围条件
   */
  private function buildRangeCondition(string $field, mixed $value): array
  {
    $conditions = [];

    $rangeValues = is_array($value) ? $value : [$value];

    if(!empty($rangeValues[0]) && ($startTimestamp = strtotime($rangeValues[0])) !== false)
    {
      $conditions[] = [$field, '>=', $startTimestamp];
    }

    if(!empty($rangeValues[1]) && ($endTimestamp = strtotime($rangeValues[1])) !== false)
    {
      $endTimestamp += 86399;

      $conditions[] = [$field, '<=', $endTimestamp];
    }

    return $conditions;
  }


  /**
   * 判断参数是否允许
   *
   * @param array $allowableList 允许的参数/字段列表
   * @param string $target 目标参数/字段
   * @return boolean 是否允许
   */
  private function isParamAllowable(array $allowableList, string $target): bool
  {
    $allowableMap = array_flip($allowableList);

    return isset($allowableMap[$target]);
  }


  /**
   * 判断是否为模糊匹配字段
   *
   * @param array $filterConfig 过滤配置
   * @param string $field 字段名
   * @return bool 是否为模糊过滤
   */
  private function isFuzzyFilter(array $filterConfig, string $field): bool
  {
    return !empty($filterConfig['fuzzy']) && in_array($field, $filterConfig['fuzzy']);
  }


  /**
   * 判断是否为左模糊匹配字段
   *
   * @param array $filterConfig 过滤配置
   * @param string $field 字段名
   * @return bool 是否为左模糊过滤
   */
  private function isLeftFuzzyFilter(array $filterConfig, string $field ): bool
  {
    return !empty($filterConfig['left_fuzzy']) && in_array($field , $filterConfig['left_fuzzy']);
  }


  /**
   * 判断是否为范围查询字段
   *
   * @param array $filterConfig 过滤配置
   * @param string $field 字段名
   * @return bool 是否为范围过滤
   */
  private function isRangeFilter(array $filterConfig, string $field): bool
  {
    return !empty($filterConfig['range']) && in_array($field, $filterConfig['range']);
  }
}
